Shader Graph着色器视图自定义节点API:Code Function Node
随着在Unity 2018.1中加入Shader Graph着色器视图,如今在Unity中创建自定义着色器变得更为简单。然而,尽管我们默认提供了不少各式各样的节点Node,这些节点仍不可能满足开发者的所有需求。因此我们开发了一个自定义节点API,提供开发者在使用C#创建新节点时使用。这个API还能帮助开发者根据需求扩展Shader Graph着色器视图。
明天晚上8点,新一期的Unity技术直播课程将详解使用Shader Graph着色器视图快速创建炫酷特效。今天我们将先介绍在Unity 2018.1 beta中一个扩展Shader Graph着色器视图的方法-使用Code Function Node,它是创建自定义节点最简单的方法。下面将介绍如何使用该方法创建一个新节点。
使用Code Function Node创建新节点
首先新创建一个C#脚本,命名为MyCustomNode。为了使用Code Function Node API,我们需要在代码中包含或是添加该类到命名空间UnityEditor.ShaderGraph,然后从基类CodeFunctionNode进行继承。
using UnityEngine;
using UnityEditor.ShaderGraph;
public class MyCustomNode : CodeFunctionNode
{
}
你可能已经注意到,MyCustomNode被高亮为一个错误。如果我们把光标移到该错误信息上,会看到提示我们需要实现一个名为GetFunctionToConvert的继承成员。基类CodeFunctionNode会告诉Shader Graph着色器视图如何处理这个节点,但我们仍需要告诉它结果函数应该是什么。
GetFunctionToConvert方法使用Reflection反射来将一个方法转换为MethodInfo的实例,从而使CodeFunctionNode能在Shader Graph着色器视图中转换和使用。并且让我们能更为直观地写出着色器函数。
如下面代码所示,添加命名空间System.Reflection和重写函数GetFunctionToConvert。要注意MyCustomFunction,它将作为函数名写入着色器中。你还能根据自己正在编写的函数为其命名,只要不以数字开头即可。在本示例中我们将使用MyCustomFunction这个名字。
using UnityEngine;
using UnityEditor.ShaderGraph;
using System.Reflection;
public class MyCustomNode : CodeFunctionNode
{
protected override MethodInfo GetFunctionToConvert()
{
return GetType().GetMethod("MyCustomFunction",
BindingFlags.Static | BindingFlags.NonPublic);
}
}
现在我们脚本中的错误都已经解决,我们可以开始编写节点的功能了!
首先我们应进行命名,在类中加入一个没有参数的公有构造函数。在该函数中,将变量名设为包含节点标题的字符串。当该节点在图中出现时,它将显示在节点的标题栏上。本示例中我们将节点命名为My Custom Node。
using UnityEngine;
using UnityEditor.ShaderGraph;
using System.Reflection;
public class MyCustomNode : CodeFunctionNode
{
public MyCustomNode()
{
name = "My Custom Node";
}
protected override MethodInfo GetFunctionToConvert()
{
return GetType().GetMethod("MyCustomFunction",
BindingFlags.Static | BindingFlags.NonPublic);
}
}
接下来,我们将定义节点的函数。如果你熟悉反射,你会注意到方法GetFunctionToConvert会尝试访问这个类中的方法MyCustomFunction。MyCustomFunction将定义着色器函数。
现在让我们创建一个静态方法,返回类型为string,它的名字与方法GetFunctionToConvert中的字符串一致。本示例中,这个名字为MyCustomFunction。在该方法的参数中,我们可以定义节点将拥有哪些端口。这些参数将直接映射到最后着色器函数的参数中。我们需要添加Shader Graph着色器视图中支持类型的参数并对其设定Slot属性。
现在我们要加入二个类型为DynamicDimensionVector的参数,分别命名为A和B,以及一个out参数,类型为DynamicDimensionVector,命名为Out。然后我们为这些参数加入默认Slot属性。每个Slot属性需要一个特别的索引和绑定,我们把它们设为None。
static string MyCustomFunction(
[Slot(0, Binding.None)] DynamicDimensionVector A,
[Slot(1, Binding.None)] DynamicDimensionVector B,
[Slot(2, Binding.None)] out DynamicDimensionVector Out)
{
}
在下面方法中,我们会在返回字符串中定义着色器函数的内容。这些内容包含着色器函数和HLSL代码。本示例中定义为Out = A + B;。我们创建好的方法如下所示:
static string MyCustomFunction(
[Slot(0, Binding.None)] DynamicDimensionVector A,
[Slot(1, Binding.None)] DynamicDimensionVector B,
[Slot(2, Binding.None)] out DynamicDimensionVector Out)
{
return
@"
{
Out = A + B;
}
";
}
}
这就是Shader Graph着色器视图中加法节点的C#代码。
在完成可用节点前,我们还要做一件事:指定它在Create Node Menu创建节点菜单的出现位置。这一步通过在类上添加Title属性来完成。这会定义一个string数组,它会描述节点在菜单层级列表中出现的位置。这个数组中最后一个字符串定义了节点在Create Node Menu中的名字。
本示例中,我们会把节点称为My Custom Node,并把它放在Custom文件夹中。
[Title("Custom", "My Custom Node")]
public class MyCustomNode : CodeFunctionNode
{
现在我们的节点就完成了!如果我们返回Unity,编译脚本然后打开Shader Graph着色器视图,我们会看到在Create Node Menu中出现了新节点。
然后在Shader Graph着色器视图中创建节点实例。你会看到它拥有和我们在MyCustomFunction类中所定义的相同名字和类型。
现在你便可以通过使用不同的接口类型和绑定来创建各种节点。这个方法返回的字符串可以包含任意在Unity常规着色器中有效的HLSL代码。下面的节点会返回三个输入值中的最小值。
static string Min3(
[Slot(0, Binding.None)] DynamicDimensionVector A,
[Slot(1, Binding.None)] DynamicDimensionVector B,
[Slot(2, Binding.None)] DynamicDimensionVector C,
[Slot(3, Binding.None)] out DynamicDimensionVector Out)
{
return
@"
{
Out = min(min(A, B), C);
}
";
}
}
下面这个节点能根据输入的布尔值反转法线。要注意在本示例中,接口Normal是如何拥有WorldSpaceNormal的绑定的。当没有任何边线(Edge)连接这个接口时,它会默认使用网格的世界空间法线向量。
注意:当使用一个具体输出类型,例如Vector 3时,我们必须在返回着色器函数前对其进行定义。
static string FlipNormal(
[Slot(0, Binding.WorldSpaceNormal)] Vector3 Normal,
[Slot(1, Binding.None)] Boolean Predicate,
[Slot(2, Binding.None)] out Vector 3 Out)
{
Out = Vector3.zero;
return
@"
{
Out = Predicate == 1 ? -1 * Normal : Normal;;
}
";
}
}
参考文档
Reflection
https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/reflection
CodeFunctionNode API
https://github.com/Unity-Technologies/ShaderGraph/wiki/CodeFunctionNode
Port Binding
https://github.com/Unity-Technologies/ShaderGraph/wiki/Port-Bindings
小结
现在你已经准备好在Shader Graph着色器视图中使用Code Function Node来创建节点了吗?当然这只是开始。要想自定义该系统,你还可以在Shader Graph着色器视图中去做更多的事情。
明天晚上8点,Unity技术经理成亮将为开发者们带来更多Shader Graph着色器视图的功能讲解,对着色器开发感兴趣的开发或美术人员一定不能错过,带上你的问题和讲师互动吧!
推荐阅读
官方活动
直播预告 | 使用Shader Graph着色器视图快速创建炫酷特效
直播时间:4月11日 20:00-21:00
活动网址:https://connect.unity.com/events/unitychina-shadergraph
活动信息:截至至4月20日 16:00
活动网址:https://connect.unity.com/challenges/universal
Unite Beijing 2018 及 Training Day
活动信息:5月11-13日 北京国家会议中心
售票官网: http://unite2018.csdn.net/ 或者直接扫描下图二维码进行购票!
点击“阅读原文”访问Unity中文官方论坛!